package com.atguigu.auth.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.auth.mapper.OaProcessMapper;
import com.atguigu.auth.service.OaProcessRecordService;
import com.atguigu.auth.service.OaProcessService;
import com.atguigu.auth.service.OaProcessTemplateService;
import com.atguigu.auth.service.SysUserService;
import com.atguigu.model.process.Process;
import com.atguigu.model.process.ProcessRecord;
import com.atguigu.model.process.ProcessTemplate;
import com.atguigu.model.system.SysUser;
import com.atguigu.security.custom.LoginUserInfoHelper;
import com.atguigu.vo.process.ApprovalVo;
import com.atguigu.vo.process.ProcessFormVo;
import com.atguigu.vo.process.ProcessQueryVo;
import com.atguigu.vo.process.ProcessVo;
import com.atguigu.wechat.service.MessageService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * @author 12646
 * @description 针对表【oa_process(审批类型)】的数据库操作Service实现
 * @createDate 2023-11-21 19:23:48
 */
@Service
public class OaProcessServiceImpl extends ServiceImpl<OaProcessMapper, Process>
    implements OaProcessService {
  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private SysUserService sysUserService;
  @Autowired
  private OaProcessTemplateService processTemplateService;
  @Autowired
  private RuntimeService runtimeService;
  @Autowired
  private TaskService taskService;
  @Autowired
  private OaProcessRecordService processRecordService;
  @Autowired
  private HistoryService historyService;
  @Autowired
  private OaProcessMapper processMapper;
  @Autowired
  private MessageService messageService;
  @Override
  public void deployByZip(String deployPath) {
    // 定义zip输入流
    InputStream inputStream = this
        .getClass()
        .getClassLoader()
        .getResourceAsStream(deployPath);
    ZipInputStream zipInputStream = new ZipInputStream(inputStream);
    // 流程部署
    Deployment deployment = repositoryService.createDeployment()
        .addZipInputStream(zipInputStream)
        .deploy();
  }
  
  @Override
  public void startUp(ProcessFormVo processFormVo) {
    // 1. 根据当前用户id获取用户信息
    SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());
    // 2. 根据审批模版id把模版信息查询
    ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId()); // 6
    // 3. 保存提交审批信息到业务表——oa_process
    Process process = new Process();
    BeanUtils.copyProperties(processFormVo, process); // 将processFormVo中的属性拷贝到process中
    String workNo = System.currentTimeMillis() + "";
    process.setProcessCode(workNo);
    process.setUserId(LoginUserInfoHelper.getUserId());
    process.setFormValues(processFormVo.getFormValues());
    process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");
    process.setStatus(1);
    baseMapper.insert(process);
    // 4. 启动流程实例 RuntimeService
    // 4.1 流程定义key
    String processDefinitionKey = processTemplate.getProcessDefinitionKey();
    // 4.2 业务key processId
    String businessKey = String.valueOf(process.getId());
    // 4.3 流程参数：form表单json数据，转换成map集合
    JSONObject jsonObject = JSON.parseObject(process.getFormValues());
    JSONObject formData = jsonObject.getJSONObject("formData");
    // 遍历formData，将数据封装到map集合
    Map<String, Object> map = new HashMap<>();
    map.putAll(formData);
    
    Map<String, Object> variables = new HashMap<>();
    variables.put("data", map);
    // 启动流程实例
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
        processDefinitionKey, businessKey, variables);
    // 5. 查询下一个审批人，可能有多个（并行审批）
    List<Task> taskList = this.getCurrentTaskList(processInstance.getId());
    if (!CollectionUtils.isEmpty(taskList)) {
      List<String> assigneeList = new ArrayList<>();
      for (Task task : taskList) {
        
        SysUser user = sysUserService.getByUsername(task.getAssignee());
        assigneeList.add(user.getName());
        
        // TODO 6. 推送消息给下一个审批人，后续完善
        messageService.pushPendingMessage(process.getId(), sysUser.getId(), task.getId());
      }
      process.setProcessInstanceId(processInstance.getId());
      process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
    }
    baseMapper.updateById(process);
    // 记录操作行为
    processRecordService.record(process.getId(), 1, "发起申请");
  }
  
  // 最主要是实现List<task>变成List<ProcessVo>
  @Override
  public IPage<ProcessVo> findPending(Page<java.lang.Process> pageParam) {
    // 1. 根据当前登录的用户名称查询
    TaskQuery query = taskService.createTaskQuery().taskAssignee(LoginUserInfoHelper.getUsername()).orderByTaskCreateTime().desc();
    // 2. 调用方法分页查询，返回list待办任务集合。 listPage：第一个参数：开始位置，第二个参数：每页记录数
    List<Task> list = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
    // 3. 封装返回list集合数据到List<ProcessVo>集合中
    List<ProcessVo> processList = new ArrayList<>();
    // 根据流程的业务ID查询实体并关联
    for (Task task : list) {
      // 获取流程实例ID
      String processInstanceId = task.getProcessInstanceId();
      // 根据流程实例id获取实例对象
      ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
      if (processInstance == null) {
        continue;
      }
      // 根据实例对象获取业务key
      String businessKey = processInstance.getBusinessKey();
      if (businessKey == null) {
        continue;
      }
      Process process = this.getById(Long.parseLong(businessKey));
      ProcessVo processVo = new ProcessVo();
      BeanUtils.copyProperties(process, processVo);
      processVo.setTaskId(task.getId());
      processList.add(processVo);
    }
    // 4. 封装返回Page对象
    long totalCount = query.count();
    
    
    IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
    page.setRecords(processList);
    return page;
  }
  
  @Override
  public Map<String, Object> show(Long id) {
    // 1. 根据流程id获取流程信息Process
    Process process = this.getById(id);
    // 2. 根据流程id获取流程记录信息
    List<ProcessRecord> processRecordList = processRecordService.list(new
        LambdaQueryWrapper<ProcessRecord>().eq(ProcessRecord::getProcessId, id));
    // 3. 根据模版id查询模版信息
    ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());
    // 4. 判断当前用户是否可以审批
    // 审批后也不能重复审批
    Map<String, Object> map = new HashMap<>();
    map.put("process", process);
    map.put("processRecordList", processRecordList);
    map.put("processTemplate", processTemplate);
    // 计算当前用户是否可以审批，能够查看详情的用户不是都能审批，审批后也不能重复审批
    boolean isApprove = false;
    List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
    if (!CollectionUtils.isEmpty(taskList)) {
      for (Task task : taskList) {
        if (task.getAssignee().equals(LoginUserInfoHelper.getUsername())) {
          isApprove = true;
        }
      }
    }
    map.put("isApprove", isApprove);
    return map;
  }
  
  @Override
  public void approve(ApprovalVo approvalVo) {
    Map<String, Object> variables1 = taskService.getVariables(approvalVo.getTaskId());
    for (Map.Entry<String, Object> entry : variables1.entrySet()) {
      System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
    }
    String taskId = approvalVo.getTaskId();
    // 判断前端传递的值是多少，如果是1，表示审批通过，如果是-1，表示审批不通过
    if (approvalVo.getStatus() == 1) {
      // 已通过
      Map<String, Object> variables = new HashMap<String, Object>();
      taskService.complete(taskId, variables);
    } else {
      // 驳回
      this.endTask(taskId);
    }
    String description = approvalVo.getStatus().intValue() == 1 ? "已通过" : "驳回";
    processRecordService.record(approvalVo.getProcessId(), approvalVo.getStatus(), description);
    
    // 计算下一个审批人
    Process process = this.getById(approvalVo.getProcessId());
    List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
    if (!CollectionUtils.isEmpty(taskList)) {
      List<String> assigneeList = new ArrayList<>();
      for (Task task : taskList) {
        SysUser sysUser = sysUserService.getByUsername(task.getAssignee());
        assigneeList.add(sysUser.getName());
        
        // 推送消息给下一个审批人
        messageService.pushPendingMessage(process.getId(), sysUser.getId(), task.getId());
      }
      process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
      process.setStatus(1);
    } else {
      if (approvalVo.getStatus().intValue() == 1) {
        process.setDescription("审批完成（同意）");
        process.setStatus(2);
      } else {
        process.setDescription("审批完成（拒绝）");
        process.setStatus(-1);
      }
    }
    // 推送消息给申请人
    this.updateById(process);
  }
  
  @Override
  public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
    // 根据当前人的ID查询
    HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().taskAssignee(LoginUserInfoHelper.getUsername()).finished().orderByTaskCreateTime().desc();
    List<HistoricTaskInstance> list = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
    long totalCount = query.count();
    
    List<ProcessVo> processList = new ArrayList<>();
    for (HistoricTaskInstance item : list) {
      String processInstanceId = item.getProcessInstanceId();
      Process process = this.getOne(new LambdaQueryWrapper<Process>().eq(Process::getProcessInstanceId, processInstanceId));
      ProcessVo processVo = new ProcessVo();
      BeanUtils.copyProperties(process, processVo);
      processVo.setTaskId("0");
      processList.add(processVo);
    }
    IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
    page.setRecords(processList);
    return page;
  }
  
  @Override
  public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
    ProcessQueryVo processQueryVo = new ProcessQueryVo();
    processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
    System.out.println(LoginUserInfoHelper.getUserId());
    IPage<ProcessVo> page = processMapper.selectPage(pageParam, processQueryVo);
    for (ProcessVo item : page.getRecords()) {
      item.setTaskId("0");
    }
    return page;
  }
  
  /**
   * 获取当前任务列表
   */
  private List<Task> getCurrentTaskList(String processInstanceId) {
    List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
    return tasks;
  }
  
  private void endTask(String taskId) {
    //  当前任务
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    
    BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
    List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
    // 并行任务可能为null
    if (CollectionUtils.isEmpty(endEventList)) {
      return;
    }
    FlowNode endFlowNode = (FlowNode) endEventList.get(0);
    FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
    
    //  临时保存当前活动的原始方向
    List originalSequenceFlowList = new ArrayList<>();
    originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
    //  清理活动方向
    currentFlowNode.getOutgoingFlows().clear();
    
    //  建立新方向
    SequenceFlow newSequenceFlow = new SequenceFlow();
    newSequenceFlow.setId("newSequenceFlowId");
    newSequenceFlow.setSourceFlowElement(currentFlowNode);
    newSequenceFlow.setTargetFlowElement(endFlowNode);
    List newSequenceFlowList = new ArrayList<>();
    newSequenceFlowList.add(newSequenceFlow);
    //  当前节点指向新的方向
    currentFlowNode.setOutgoingFlows(newSequenceFlowList);
    
    //  完成当前任务
    taskService.complete(task.getId());
  }
}